
/**
Copyright 2008, Mark Arvidson

This file is part of EwaScorecard.

EwaScorecard is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

EwaScorecard is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with EwaScorecard.  If not, see <http://www.gnu.org/licenses/>.
 */
import java.util.*;
import javax.microedition.lcdui.*;

public class ViewScoresForm extends Canvas implements CommandListener {

    private class Tab {

        int left, top, bottom, right;
    }
    private Command okCommand = new Command("Enter", "Enter Scores", Command.OK, 0);
    private Command addGymnastCommand = new Command("Add", "Add Gymnast", Command.SCREEN, 2);
    private Command createMeetCommand = new Command("Create", "New Meet", Command.SCREEN, 3);
    private Command loadMeetCommand = new Command("Load", Command.SCREEN, 4);
    private Command saveMeetCommand = new Command("Save", Command.SCREEN, 1);
    private Command saveDefaultsCommand = new Command("Store Defaults", Command.SCREEN, 5);
    private Command aboutCommand = new Command("About", Command.SCREEN, 6);
    private Command exitCommand = new Command("Quit", Command.EXIT, 7);
    private MeetData meetData;
    private EwaScorecard host;
    private int offsetX;
    private int selectedRow = 0;
    private int selectedColumn = 0;
    private CellPainter[][] cells;
    private int leftMostColumn;
    private int topMostRow;
    // store some common values
    private int cellWidth,  cellHeight,  halfWidth;
    private int rightMost,  screenHeight,  screenWidth,  widestName;
    private Font nameFont = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_PLAIN, Font.SIZE_SMALL);
    private Font scoreFont = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
    private Font aaFont = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_SMALL);
    private static final int CELL_PADDING = 4;
    private static final int HEADER_LINES = 1;
    private TeamData activeTeam;
    private int activeTeamIndex;
    private Tab[] tabs;

    public ViewScoresForm(EwaScorecard host, MeetData meetData) {
        this.meetData = meetData;
        this.host = host;
        addCommand(okCommand);
        addCommand(addGymnastCommand);
        addCommand(createMeetCommand);
        addCommand(loadMeetCommand);
        addCommand(saveMeetCommand);
        addCommand(saveDefaultsCommand);
        addCommand(aboutCommand);
        addCommand(exitCommand);
        setCommandListener(this);

        cellWidth = scoreFont.stringWidth("00.000") + CELL_PADDING;
        cellHeight = scoreFont.getHeight() + CELL_PADDING;
        halfWidth = cellWidth / 2;
        setTitle(meetData.getMeetName());
        screenHeight = getHeight();
        screenWidth = getWidth();
        tabs = new Tab[meetData.getTeams().length];
        for (int tabIndex = 0; tabIndex < tabs.length; tabIndex++) {
            tabs[tabIndex] = new Tab();
        }
    }

    private void initCells(int activeTeamIndex) {
        if (this.activeTeamIndex != activeTeamIndex || cells == null) {
            this.activeTeamIndex = activeTeamIndex;
            activeTeam = meetData.getTeam(activeTeamIndex);
            int rowCount = activeTeam.rowCount();
            int colCount = meetData.numberOfEvents() + 1;
            cells = new CellPainter[rowCount][colCount];
            for (int i = 0; i < rowCount; i++) {
                for (int j = 0; j < colCount; j++) {
                    cells[i][j] = new CellPainter(activeTeam, i, j);
                }
            }
            CellPainter.cellWidth = cellWidth;
            CellPainter.cellHeight = cellHeight;
        }
    }

    protected void paint(Graphics g) {
        initCells(activeTeamIndex);
        eraseBackground(g);
        g.setColor(0xCCFFCC);
        g.fillRect(0, 0, g.getClipWidth(), g.getClipHeight());
        g.setColor(0x000000);

        g.setFont(aaFont);
        g.drawString(formatDate(meetData.getMeetDate()), 0, 0, Graphics.TOP | Graphics.LEFT);

        int labelWidth = g.getFont().stringWidth("3rd") + 2;
        int clipWidth = g.getClipWidth();
        g.setColor(CellPainter.FIRST_COLOR);
        g.fillRect(clipWidth - labelWidth * 3, 0, labelWidth, cellHeight);
        g.setColor(0xFFFFFF);
        g.drawString("1st", g.getClipWidth() - labelWidth * 2, 2, Graphics.TOP | Graphics.RIGHT);
        g.setColor(CellPainter.SECOND_COLOR);
        g.fillRect(clipWidth - labelWidth * 2, 0, labelWidth, cellHeight);
        g.setColor(0xFFFFFF);
        g.drawString("2nd", g.getClipWidth() - labelWidth, 2, Graphics.TOP | Graphics.RIGHT);
        g.setColor(CellPainter.THIRD_COLOR);
        g.fillRect(clipWidth - labelWidth, 0, labelWidth, cellHeight);
        g.setColor(0xFFFFFF);
        g.drawString("3rd", g.getClipWidth() - 1, 2, Graphics.TOP | Graphics.RIGHT);
        g.setColor(0x000000);

        int tabTop = cellHeight * HEADER_LINES;
        int teamLeft = 0;
        int tabBottom = tabTop + cellHeight - 1;
        int teamCount = meetData.teamCount();
        for (int teamIndex = 0; teamIndex < teamCount; teamIndex++) {
            teamLeft = drawTeam(teamIndex, teamLeft, tabBottom, tabTop, g);
        }
        g.drawLine(teamLeft, tabTop, teamLeft, tabBottom);
        g.drawLine(teamLeft, tabBottom, g.getClipWidth(), tabBottom);
    }

    private int drawTeam(int teamIndex, int teamLeft, int tabBottom, int tabTop, Graphics g) {
        TeamData teamData = (TeamData) meetData.getTeam(teamIndex);
        String teamName = teamData.getName();
        int nameWidth = aaFont.stringWidth(teamName);
        int teamRight = teamLeft + nameWidth + 6;
        tabs[teamIndex].bottom = tabBottom;
        tabs[teamIndex].top = tabTop;
        tabs[teamIndex].left = teamLeft;
        tabs[teamIndex].right = teamRight;
        g.setFont(aaFont);
        g.drawString(teamName, teamLeft + 3, tabTop + 2, Graphics.TOP | Graphics.LEFT);
        if (teamIndex == activeTeamIndex) {
            drawTeamTable(g, meetData, teamData, tabBottom);
            // draw top tab edge
            g.drawLine(teamLeft, tabTop - 1, teamRight, tabTop - 1);
            if (selectedRow < 0) {
                g.setStrokeStyle(Graphics.DOTTED);
                g.drawRect(teamLeft + 1, tabTop, nameWidth + 4, cellHeight);
                g.setStrokeStyle(Graphics.SOLID);
            }
        } else {
            // only draw the bottom of the tab if it's not the active tab
            g.drawLine(teamLeft, tabBottom, teamRight, tabBottom);
            // draw top tab edge
            g.drawLine(teamLeft, tabTop, teamRight, tabTop);
        }
        g.drawLine(teamLeft, tabTop, teamLeft, tabBottom);
        return teamRight;
    }

    /** Draws team data in a grid, with team score at top. 
     * Returns bottom pixel of grid. */
    private int drawTeamTable(Graphics g, MeetData meetData, TeamData teamData, int top) {
        int rowCount = teamData.rowCount();
        int eventCount = meetData.numberOfEvents();

        // find widest name
        widestName = aaFont.stringWidth("Name");
        for (int i = 0; i < rowCount; i++) {
            int candidate = nameFont.stringWidth(teamData.getGymnast(i).getName());
            if (candidate > widestName) {
                widestName = candidate;
            }
        }
        // last drawn team defines bottom
        int aaWidth = aaFont.stringWidth("000.000");
        int tableBottom = cellHeight * (rowCount + 1 - topMostRow);
        rightMost = widestName + (cellWidth * (eventCount - leftMostColumn)) + aaWidth;

        g.setFont(aaFont);
        String score = CellPainter.formatFloat(new Float(teamData.getTeamScore()).floatValue(), 4);
        g.drawString("Team Score: " + score, 0, top, Graphics.TOP | Graphics.LEFT);

        // draw header and vertical lines  
        top += cellHeight;
        g.translate(0, top);
        // tell the cells how to paint
        CellPainter.leftCell = widestName;
        CellPainter.topCell = top + cellHeight;
        CellPainter.leftMostColumn = leftMostColumn;
        CellPainter.topMostRow = topMostRow;

        // column headers
        g.drawString("Name", 2, 2, Graphics.TOP | Graphics.LEFT);
        for (int j = leftMostColumn; j < eventCount; j++) {
            int left = ((j - leftMostColumn) * cellWidth) + widestName;
            g.drawString(meetData.getEvents()[j], left + halfWidth, 2, Graphics.TOP | Graphics.HCENTER);
            g.drawLine(left, 0, left, tableBottom);
        }
        int aaLeft = rightMost - aaWidth;
        g.drawString("AA", aaLeft + (aaWidth / 2), 2, Graphics.TOP | Graphics.HCENTER);
        g.drawLine(aaLeft, 0, aaLeft, tableBottom);
        g.drawLine(rightMost, 0, rightMost, tableBottom);

        // some horizontal lines
        g.drawLine(0, 0, rightMost, 0);
        g.drawLine(0, cellHeight - 1, rightMost, cellHeight - 1);
        g.drawLine(0, cellHeight, rightMost, cellHeight);

        // draw row data
        Vector gymnasts = teamData.gymnasts();
        for (int i = topMostRow; i < rowCount; i++) {
            drawGymnast(g, meetData, (GymnastData) gymnasts.elementAt(i), i);
        }
        g.drawLine(0, tableBottom, rightMost, tableBottom);

        g.translate(0, -top);
        return tableBottom;
    }

    protected void pointerPressed(int x, int y) {
        // Check names column first
        if (x <= widestName && y >= cellHeight * 4) {
            host.editGymnast(activeTeam,
                    activeTeam.getGymnast((y -
                    ((4 + topMostRow) * cellHeight)) / cellHeight));
        } else {
            CellPainter cell = isHitCell(x, y);
            if (cell != null) {
                selectedRow = cell.getRow();
                selectedColumn = cell.getColumn();
                repaint();
                serviceRepaints();
                host.enterScore(selectedRow, selectedColumn, activeTeamIndex);
            } else {
                // try team tabs
                for (int tabIndex = 0; tabIndex < tabs.length; tabIndex++) {
                    Tab tab = tabs[tabIndex];
                    if (x >= tab.left && x <= tab.right &&
                            y >= tab.top && y <= tab.bottom) {
                        initCells(tabIndex);
                        repaint();
                        serviceRepaints();
                        break;
                    }

                }
            }
        }
    }

    private CellPainter isHitCell(int x, int y) {
        initCells(activeTeamIndex);
        int rowCount = activeTeam.rowCount();
        int eventCount = activeTeam.getNumberOfEvents();
        for (int i = 0; i < rowCount; i++) {
            for (int j = 0; j < eventCount; j++) {
                if (cells[i][j].isHit(x, y)) {
                    return cells[i][j];
                }
            }
        }
        return null;
    }

    private String formatDate(Date input) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(input);
        String dd = String.valueOf(cal.get(Calendar.DATE));
        String mm = String.valueOf(cal.get(Calendar.MONTH) + 1);
        String yy = String.valueOf(cal.get(Calendar.YEAR));
        return mm + "/" + dd + "/" + yy;
    }

    private void drawGymnast(Graphics g, MeetData meetData,
            GymnastData gymnast, int i) {
        int eventCount = meetData.numberOfEvents();
        int dispRow = i - topMostRow + 1;
        g.setColor(0xFFFFFF);
        //g.fillRect(-offsetX, (dispRow * cellHeight), cellWidth - 1, cellHeight);
        g.setColor(0x000000);
        g.setFont(nameFont);
        int nameTop = dispRow * cellHeight;
        g.drawString(gymnast.getName(), -offsetX, nameTop + 2,
                Graphics.TOP | Graphics.LEFT);
        if (selectedColumn < 0 && selectedRow == i) {
            g.setStrokeStyle(Graphics.DOTTED);
            g.drawRect(0, nameTop + 1, widestName - 1, cellHeight - 2);
            g.setStrokeStyle(Graphics.SOLID);
        }

        g.setFont(scoreFont);
        // add one for all around
        for (int j = leftMostColumn; j < eventCount + 1; j++) {
            if (j == eventCount) {
                g.setFont(aaFont);
            }

            cells[i][j].paint(g, selectedRow, selectedColumn);
            g.drawLine(0, dispRow * cellHeight, rightMost, (dispRow) * cellHeight);
        }
    }

    private void eraseBackground(Graphics g) {
        int color = g.getColor();
        g.setColor(0xFFFFFF);
        g.fillRect(g.getClipX(), g.getClipY(), g.getClipWidth(), g.getClipHeight());
        g.setColor(color);
    }

    protected void keyPressed(int keycode) {
        switch (getGameAction(keycode)) {
            case UP:
                selectedRow -= 1;
                if (selectedRow < -1) {
                    selectedRow = -1;
                }
                ensureSelectionVisible();
                repaint();
                break;
            case DOWN:
                selectedRow += 1;
                if (selectedRow > activeTeam.rowCount() - 1) {
                    selectedRow = activeTeam.rowCount() - 1;
                }
                ensureSelectionVisible();
                repaint();
                break;
            case LEFT:
                if (selectedRow < 0) {
                    int newActiveTeamIndex = activateTeam(-1);
                    initCells(newActiveTeamIndex);
                } else {
                    selectedColumn -= 1;
                    if (selectedColumn < -1) {
                        selectedColumn = -1;
                    }
                }
                ensureSelectionVisible();
                repaint();
                break;
            case RIGHT:
                if (selectedRow < 0) {
                    int newActiveTeamIndex = activateTeam(+1);
                    initCells(newActiveTeamIndex);
                } else {
                    selectedColumn += 1;
                    if (selectedColumn > meetData.numberOfEvents()) {
                        selectedColumn = meetData.numberOfEvents();
                    }
                }
                ensureSelectionVisible();
                repaint();
                break;
            case FIRE:
                if (selectedRow >= 0) {
                    if (selectedColumn < 0) {
                        host.editGymnast(activeTeam,
                                activeTeam.getGymnast(selectedRow));
                    } else {
                        if (selectedColumn < meetData.numberOfEvents()) {
                            host.enterScore(selectedRow, selectedColumn, activeTeamIndex);
                        }
                    }
                }
                break;
        }
    }

    private void ensureSelectionVisible() {
        int topRow = (HEADER_LINES + 3) * cellHeight;
        if (selectedRow < topMostRow) {
            topMostRow = Math.max(0, selectedRow);
        } else {
            while ((((selectedRow - topMostRow + 1) * cellHeight) + topRow) > screenHeight) {
                topMostRow++;
            }
        }
        int leftCol = widestName;
        if (selectedColumn < leftMostColumn) {
            leftMostColumn = Math.max(0, selectedColumn);
        } else {
            while ((((selectedColumn - leftMostColumn + 1) * cellWidth) + leftCol) > screenWidth) {
                leftMostColumn++;
            }
        }
    }

    protected void keyRepeated(int keycode) {
        keyPressed(keycode);
    }

    public void commandAction(Command c, Displayable d) {
        if (c == okCommand) {
            if (selectedColumn < 0 && selectedRow > 0) {
                host.editGymnast(activeTeam, activeTeam.getGymnast(selectedRow));
            } else {
                host.enterScore(selectedRow, selectedColumn, activeTeamIndex);
            }
        } else if (c == addGymnastCommand) {
            host.addGymnast();
        } else if (c == createMeetCommand) {
            host.createMeet();
        } else if (c == loadMeetCommand) {
            host.loadMeetData();
        } else if (c == saveMeetCommand) {
            host.saveMeetData();
        } else if (c == saveDefaultsCommand) {
            host.saveAsDefaults();
        } else if (c == aboutCommand) {
            host.showAbout();
        } else if (c == exitCommand) {
            host.commandAction(c, this);
        }
    }

    /**
     * Change the active team by the given offset.
     * @param offset, negative moves left
     * @return the adjusted index
     */
    public int activateTeam(int offset) {
        int newActiveTeamIndex = activeTeamIndex + offset;
        if (newActiveTeamIndex < 0) {
            newActiveTeamIndex = 0;
        } else {
            if (newActiveTeamIndex >= meetData.teamCount()) {
                newActiveTeamIndex = meetData.teamCount() - 1;
            }
        }
        return newActiveTeamIndex;
    }
}
